// Copyright (c) 2024 刻BITTER
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.


#pragma once

/**
 * @brief  适用于操作STM32 风格的GPIO 外设寄存器
 *
 * 注意：不兼容STM32F1 的LL 库
 */


#include <cassert>

#include "ioxx_common.hpp"
#include "ioxx_include_device.hpp"  // 引入CMSIS 器件头文件，可能需要用户定义的配置文件


namespace ioxx {

    class Pin {
       private:
        GPIO_TypeDef *_port;
        uint16_t _pin_mask;

        uint8_t _pin_num;

       public:
        constexpr Pin(GPIO_TypeDef *port_base, uint8_t pin_num) :
            _port(port_base),
            _pin_mask(0x0001 << pin_num),
            _pin_num(pin_num) {}

        constexpr Pin(GPIO_TypeDef *port_base, uint8_t pin_num, uint16_t pin_mask) :
            _port(port_base),
            _pin_num(pin_num),
            _pin_mask(pin_mask) {}


        /**
         * @brief 从寄存器指针构造PinToken，使用了reinterpret_cast，所以不能是constexpr
         */
        //        Pin(GPIO_TypeDef *port_pointer, uint16_t pin_mask) :
        //            _port(port_pointer),
        //            _pin_mask(pin_mask) {}

        GPIO_TypeDef *port() const {
            return _port;
        }

        uint16_t pin_mask() const {
            return _pin_mask;
        }

        uint8_t pin_num() const {
            return _pin_num;
        }

        void set() const {
            port()->BSRR = pin_mask();
        }

        void set(bool level) const {
            if (level) {
                set();
            }
            else {
                clr();
            }
        }

        void clr() const {
            port()->BRR = pin_mask();
        }

        auto get() const {
            return static_cast<uint16_t>(port()->IDR) & pin_mask();
        }

        void toggle() const {
            port()->ODR ^= pin_mask();
        }
    };


    inline void setpin(const Pin &p) {
        p.set();
    }


    inline void clrpin(const Pin &p) {
        p.clr();
    }


    inline auto getpin(const Pin &p) {
        return p.get();
    }


    inline void toggle_pin(const Pin &p) {
        p.toggle();
    }


    /**
     * @brief 将PinMask 转换成pin num
     *
     * 如果输入值是常量，switch case 更容易被编译器优化，
     * 就算没被优化，switch case 运行速度也不会比循环慢。
     *
     * @param pin_mask
     * @return constexpr unsigned
     */
    constexpr unsigned from_pin_mask_to_num(uint16_t pin_mask) {
        // 复杂的constexpr 函数，C++17
        switch (pin_mask) {
            case (0x1 << 0):
                return 0;

            case (0x1 << 1):
                return 1;

            case (0x1 << 2):
                return 2;

            case (0x1 << 3):
                return 3;

            case (0x1 << 4):
                return 4;

            case (0x1 << 5):
                return 5;

            case (0x1 << 6):
                return 6;

            case (0x1 << 7):
                return 7;

            case (0x1 << 8):
                return 8;

            case (0x1 << 9):
                return 9;

            case (0x1 << 10):
                return 10;

            case (0x1 << 11):
                return 11;

            case (0x1 << 12):
                return 12;

            case (0x1 << 13):
                return 13;

            case (0x1 << 14):
                return 14;

            case (0x1 << 15):
                return 15;
            default:
                #ifdef USE_FULL_ASSERT
            assert_failed(const_cast<uint8_t *>(reinterpret_cast<const uint8_t*>("common_cm_pin: invalid pin mask")), 168);
                #endif
                return 0;
        }
    }


    /**
     * @brief 可以用GPIO_TypeDef 和PinMask 初始化的Pin 对象
     *
     */
    class PinMask : public Pin {
    public:
        constexpr PinMask(GPIO_TypeDef *port_base, uint16_t pin_mask) :
            Pin(port_base, from_pin_mask_to_num(pin_mask), pin_mask) {}
    };

}  // namespace ioxx